<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="root">
        <div>
            <div>{{name}}</div>
            <div>{{age}}}</div>
            <div>hello3</div>
            <ul>
                <li>li1</li>
                <li>li2</li>
                <li>li3</li>
            </ul>
        </div>
    </div>
    <script>
        function JGVue(options){
            this._data = options.data
            this._template = document.querySelector(options.el)

            this.mount()  //挂载
        }

        class VNode{
            constructor(tag,data,value,type){
                this.tag = tag && tag.toLowerCase()
                this.data = data
                this.value = value
                this.type = type
                this.children = []
            }
            appendChild(vnode){
                this.children.push(vnode)
            }
        }

        //根据HTML  DOM生成虚拟dom
        function getVNode(node){
            let type = node.nodeType
            let _vnode = null
            if(type === 1){  // 元素节点
                let nodeName = node.nodeName
                let attrs = node.attributes
                let attrsObj = {}
                for(let i = 0; i < attrs.length; i ++){
                    attrsObj[attrs[i].nodeName] = attrs[i].nodeValue
                }
                _vnode= new VNode(nodeName,attrsObj,undefined,type)
                let children = node.childNodes
                for(let i = 0; i < children.length; i++){
                    _vnode.appendChild(getVNode(children[i]))
                }
            }else if(type === 3){  //文本节点
                _vnode = new VNode(undefined,undefined,node.nodeValue,type)
            }
            return _vnode
        }

        
        function getValueByPath(obj,path){
            let paths = path.split(',')
            let res = obj
            let prop
            while(prop = paths.shift()){
                res = res[prop]
            }
            return res
        }

        let r = /\{\{(.+?)\}\}/g
        //将虚拟dom与数据结合
        function compiler(vnode,data){
            let _type = vnode.type
            let _data = vnode.data
            let _value = vnode.value
            let _tag = vnode.tag
            let children = vnode.children

            let _vnode = null
            if(_type === 1){// 元素节点
                _vnode = new VNode(_tag,_data,_value,_type)
                children.forEach(subnode => {
                    _vnode.appendChild(compiler(subnode,data))

                });
            }else if(_type === 3){  //文本节点
                _value = _value.replace(r,function(a,b){
                    return getValueByPath(data,b.trim())
                })
                _vnode = new VNode(_tag,_data,_value,_type)
            }
            return _vnode
        }

        JGVue.prototype.mount = function(){
            //提供一个render函数，生成虚拟dom
            this.render = this.createRenderFn()   //这个函数使用来缓存抽象语法树的

            this.mountComponent()
        }

        JGVue.prototype.mountComponent = function(){
            let mount = () => {
                this.update(this.render())
            }
            mount.call(this)
        }

        //生成render函数，目的时来缓存抽象语法树（这里使用虚拟dom来模拟抽象语法树）（运用到了函数科里化，返回值是一个函数）
        JGVue.prototype.createRenderFn = function(){
            let ast = getVNode(this._template)

            return function render(){
                //获取到带有数据的虚拟dom结构
                let _temp = compiler(ast,this._data)
                debugger
                console.log(_temp)
                return _temp
            }
        }
        
        //将虚拟dom渲染到页面（含有diff算法）
        JGVue.prototype.update = function(){

        }


        let app = new JGVue({
            el:'#root',
            data:{
                name:'张三',
                age:'18'
            }
        })

        /* 
        这段代码主要：
        写了一个JGvue构造函数，将之后所有的操作都封装在这个构造函数中，
        写了一个createRenderFun（）来生成render函数，并缓存了抽象语法树，这里用虚拟dom来模拟抽象语法树，
        render函数主要来生成虚拟dom，定义一个update函数，来将虚拟dom渲染到页面，同时还定义了一个函数来根据HTML  DOM
        来生成虚拟dom，有定义了一个函数来将JGVue实例对象中的数据与虚拟dom结合，生成一个有数据的虚拟dom



        在这里使用都了函数科里化，主要思想是将函数中比较消耗性能的部分给缓存起来，运用到遇上的代码就是将抽象语法树给缓存起来了
        即将虚拟dom缓存起来


        上述代码整体描述:
        首先要注意的是虚拟dom与页面中的真实dom是一一对应的关系，即，当页面模板结构改变时，虚拟dom就会改变
        当new的JGVue实例对象中的数据发生改变时，就会生成新的虚拟dom，并且缓存虚拟dom，之后会进行diff算法将洗的虚拟dom与原
        来的虚拟dom进行比较，忽略没哟改变的结构，更新发生改变的结构，更新完成之后也会与页面进行一一对应，
        这样页面结构也会随之更新
        */
    </script>
</body>
</html>